//
//  ViewController.swift
//  UIKitApp
//
//  Created by changlei on 2021/11/4.
//
//

import UIKit
import Kingfisher
import MJRefresh


fileprivate let spacing = 4.0
fileprivate let radius = 8.0
fileprivate let pageSize = 20
fileprivate let cellId = "SubjectCell"
fileprivate let cellMinimumWidth = 134.0

class SearchResultsController: BaseController {
    private var queryText: String?
    private var subjects: [Subject] = []
    private var nextPage: Int = 0
    private var task: Task<Any, Never>?

    lazy private var collectionView: UICollectionView = {
        let layout = UICollectionViewWaterfallLayout()
        layout.minimumLineSpacing = 0
        layout.minimumInteritemSpacing = 0
        layout.delegate = self

        let uiCollectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
        uiCollectionView.register(SubjectCell.self, forCellWithReuseIdentifier: cellId)
        uiCollectionView.backgroundColor = .clear
        uiCollectionView.keyboardDismissMode = .onDrag
        uiCollectionView.delegate = self
        uiCollectionView.dataSource = self
        uiCollectionView.contentInset = UIEdgeInsets(top: spacing, left: spacing, bottom: spacing, right: spacing)
        return uiCollectionView
    }()
    lazy private var header: MJRefreshHeader = {
        let refreshHeader = MJRefreshNormalHeader(refreshingBlock: refresh)
        refreshHeader.setTitle("正在刷新", for: .idle)
        refreshHeader.setTitle("松开刷新", for: .pulling)
        refreshHeader.setTitle("正在刷新", for: .willRefresh)
        refreshHeader.setTitle("正在刷新", for: .refreshing)
        refreshHeader.lastUpdatedTimeLabel?.isHidden = true
        return refreshHeader
    }()
    lazy private var footer: MJRefreshFooter = {
        let refreshFooter = MJRefreshAutoGifFooter(refreshingBlock: loadNext)
        refreshFooter.setTitle("上拉加载", for: .idle)
        refreshFooter.setTitle("上拉加载", for: .pulling)
        refreshFooter.setTitle("上拉加载", for: .willRefresh)
        refreshFooter.setTitle("正在加载…", for: .refreshing)
        refreshFooter.setTitle("我是有底线的", for: .noMoreData)
        return refreshFooter
    }()
    private let noResults: UILabel = {
        let uiLabel = UILabel()
        uiLabel.text = "无结果"
        uiLabel.textColor = .placeholderText
        uiLabel.textAlignment = .left
        uiLabel.numberOfLines = 0
        uiLabel.font = .systemFont(ofSize: 27, weight: .medium)
        return uiLabel
    }()
    private let indicator: UIActivityIndicatorView = {
        let indicatorView = UIActivityIndicatorView()
        indicatorView.style = .medium
        indicatorView.color = .label
        indicatorView.hidesWhenStopped = true
        return indicatorView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(collectionView)
        collectionView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }

        collectionView.mj_header = header
        header.beginRefreshing()
    }

    func query(text: String?) {
        if queryText == text {
            return
        }
        queryText = text
        refresh()
    }

    func refresh() {
        if subjects.isEmpty {
            showIndicator()
        }
        task?.cancel()
        task = Task(operation: onRefresh)
    }

    func loadNext() {
        task?.cancel()
        task = Task(operation: onLoadNext)
    }

    @Sendable func onRefresh() async {
        await onRequest(0, pageSize)
    }

    @Sendable func onLoadNext() async {
        await onRequest(nextPage, pageSize)
    }

    @Sendable func onRequest(_ currentPage: Int, _ pageSize: Int) async {
        let subjects = await Api.getSubjects(tag: queryText ?? "", currentPage: currentPage, pageSize: pageSize) ?? []
        nextPage = currentPage
        DispatchQueue.main.async {
            self.header.endRefreshing()
            self.footer.endRefreshing()
            let isFirst = currentPage == 0
            let hasNext = subjects.count >= pageSize
            if hasNext {
                self.collectionView.mj_footer = self.footer
                self.nextPage += 1
            } else {
                if isFirst {
                    self.collectionView.mj_footer = nil
                } else {
                    self.footer.endRefreshingWithNoMoreData()
                }
            }
            if isFirst {
                self.subjects.removeAll()
            }
            self.subjects.append(contentsOf: subjects)
            self.collectionView.reloadData()
            self.hideIndicator()
            if isFirst && !subjects.isEmpty {
                self.collectionView.scrollToItem(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
            }
        }
    }

    func showIndicator() {
        if indicator.superview == nil {
            view.addSubview(indicator)
            indicator.snp.makeConstraints { make in
                make.size.lessThanOrEqualToSuperview()
                make.center.equalToSuperview()
            }
        }
        indicator.startAnimating()
        UIView.animate(withDuration: 0.3) {
            self.indicator.alpha = 1
            self.noResults.alpha = 0
        }
    }

    func hideIndicator() {
        let isEmpty = subjects.isEmpty
        if noResults.superview == nil && isEmpty {
            view.addSubview(noResults)
            noResults.snp.makeConstraints { make in
                make.size.lessThanOrEqualToSuperview()
                make.center.equalToSuperview()
            }
        }
        UIView.animate(withDuration: 0.3, animations: {
            self.indicator.alpha = 0
            self.noResults.alpha = isEmpty ? 1 : 0
        }, completion: { b in
            self.indicator.stopAnimating()
            self.indicator.removeFromSuperview()
            if !isEmpty {
                self.noResults.removeFromSuperview()
            }
        })
    }
}

extension SearchResultsController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewWaterfallLayoutDelegate {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        subjects.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! SubjectCell
        cell.setSubject(subjects[indexPath.row])
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
        guard let cell = collectionView.cellForItem(at: indexPath) else {
            return
        }
        UIView.animate(withDuration: 0.15) {
            cell.transform = CGAffineTransform.identity.scaledBy(x: 1.02, y: 1.02)
        }
    }

    func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
        guard let cell = collectionView.cellForItem(at: indexPath) else {
            return
        }
        UIView.animate(withDuration: 0.15) {
            cell.transform = CGAffineTransform.identity
        }
    }

    public func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
        let subject = subjects[indexPath.row]
        return subject.contextMenuConfiguration
    }

    public func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
        animator.addCompletion {
            var controller: UIViewController!
            if let previewController = animator.previewViewController {
                controller = previewController
            } else {
                controller = (configuration.identifier as! Viewable).detailController
            }
            self.presentingViewController?.navigationController?.pushViewController(controller, animated: true)
            if let controller = controller as? ViewableChromeController {
                controller.isReadOnly = false
            }
        }
    }

    func collectionView(_ collectionView: UICollectionView, heightForItemAt indexPath: IndexPath) -> CGFloat {
        let subject = subjects[indexPath.row]
        return (collectionView.bounds.width / CGFloat(numberOfColumns(collectionView))) * subject.coverY / subject.coverX
    }

    public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let subject = subjects[indexPath.row]
        presentingViewController?.navigationController?.pushViewController(subject.detailController, animated: true)
    }

    func numberOfColumns(_ collectionView: UICollectionView) -> Int {
        Int(collectionView.bounds.width / cellMinimumWidth)
    }
}

class SubjectCell: UICollectionViewCell {
    private let container: UIView = {
        let uiView = UIView()
        uiView.layer.cornerRadius = radius
        uiView.clipsToBounds = true
        uiView.layer.borderColor = UIColor.separator.cgColor
        uiView.layer.borderWidth = 0.2
        return uiView
    }()
    private let avatar: CoverView = {
        CoverView()
    }()
    private let title: UILabel = {
        let uiLabel = UILabel()
        uiLabel.textColor = .white
        uiLabel.font = .systemFont(ofSize: 12, weight: .medium)
        return uiLabel
    }()

    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        var configuration = UIBackgroundConfiguration.clear()
        configuration.backgroundColor = .systemBackground
        configuration.cornerRadius = radius
        configuration.backgroundInsets = NSDirectionalEdgeInsets(top: spacing, leading: spacing, bottom: spacing + 0.2, trailing: spacing)
        backgroundConfiguration = configuration

        contentView.addSubview(container)
        container.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(NSDirectionalEdgeInsets(top: spacing, leading: spacing, bottom: spacing, trailing: spacing))
        }

        container.addSubview(avatar)
        container.addSubview(title)

        avatar.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }

        let titleBackground = UIView()
        titleBackground.backgroundColor = .black.withAlphaComponent(0.6)
        container.addSubview(titleBackground)
        titleBackground.snp.makeConstraints { make in
            make.width.equalToSuperview()
            make.bottom.equalTo(avatar)
            make.height.equalTo(32)
        }

        titleBackground.addSubview(title)
        title.snp.makeConstraints { make in
            make.centerY.equalTo(titleBackground)
            make.leading.trailing.equalToSuperview().inset(NSDirectionalEdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8))
        }
    }

    func setSubject(_ value: Subject) {
        avatar.setImage(with: value.cover)
        title.text = value.title
    }
}